package uk.ac.bbk.dcs.pakdd2014;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.math.*;

import org.apache.commons.math3.analysis.function.Gaussian;

public class Distribution implements java.io.Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6216607876742448904L;
	private double populationSize;
	private HashMap<Integer,Double> histogram;
	private double S;
	private Double M;
	
	private void addToHistogram(int d, double p) {
		if(histogram.containsKey(d)) {
			histogram.put(d, histogram.get(d) + p);
		}
		else {
			histogram.put(d, p);
		}
	}
	
	public Distribution()
	{
		M = null;
		S = 0;
		histogram = new HashMap<Integer,Double>();
		populationSize = 0;
	}
	
	public void include(int d, double f)
	{
		populationSize += f;
		addToHistogram(d, f);
		if(M == null) {
			M = (double) d;
			S = 0;
		}
		else {
			double M1 = M + ((double)d - M) * f / populationSize;
			S += ((double)d - M) * ((double)d - M1);
			M = M1;
		}
	}
	
	public double var() {
		double m = mean();
		double errorsum = 0;
		for(Map.Entry<Integer,Double> entry : histogram.entrySet())
		{
			errorsum += entry.getValue() * Math.pow(entry.getKey() - m,2);
		}
		return errorsum / populationSize;
		//return (populationSize > 0 ? S / populationSize : 0.1);
	}
	
	public double stdev() {
		return Math.sqrt(var());
	}
	
	public double mean(){
		return (populationSize > 0 ? M : 0);
	}
	
	public double prob(double x)
	{
		double s = stdev();
		if(s == 0)
		{
			s = 0.1;
		}
		Gaussian g = new Gaussian(mean(), s);
		return g.value(x);
	}
	
	public List<Entry<Integer, Double>> topFrequencies() {
        List<Map.Entry<Integer, Double>> list = new LinkedList<Map.Entry<Integer, Double>>(histogram.entrySet());

        Collections.sort(list, new Comparator<Map.Entry<Integer, Double>>() {
            public int compare(Map.Entry<Integer, Double> m1, Map.Entry<Integer, Double> m2) {
                return (m2.getValue()).compareTo(m1.getValue());
            }
        });
		
        return list;
	}
}